﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GameLib.Gameplay.Scripting
{
	public class ScriptList
	{
		public struct ScriptListItem
		{
			public ScriptListItem(ScriptInstance script, int level)
			{
				Script = script;
				Level = level;
			}

			public ScriptInstance Script;
			public int Level;

			public static readonly ScriptListItem Empty = new ScriptListItem(null, -1);
		}

		public void Add(ScriptInstance script)
		{
			int currentLevel = CurrentScript.Level;
			int insertLevel = currentLevel + 1;

			// find insert position (before the first one with lower level, starting after the current index)
			int insertPos = m_CurrentIndex + 1;
			for (; insertPos < m_Scripts.Count; insertPos++)
			{
				if (m_Scripts[insertPos].Level < insertLevel)
					break;
			}
			m_Scripts.Insert(insertPos, new ScriptListItem(script, insertLevel));
		}

		public void Update(double deltaTime)
		{
			// start at the first script if not pointing to any yet
			if (m_CurrentIndex < 0 && !IsEmpty)
			{
				m_CurrentIndex = 0;
				m_Scripts[0].Script.Started();
			}

			if (m_CurrentIndex >= 0)
			{
				ScriptUpdateResults result = m_Scripts[m_CurrentIndex].Script.Update(deltaTime);

				if (result == ScriptUpdateResults.Completed)
				{
					FinishCurrent();
				}
				else if (result == ScriptUpdateResults.Paused)
				{
					PauseCurrent();
				}
			}
		}

		public bool IsEmpty
		{
			get { return m_Scripts.Count == 0; }
		}

		private ScriptListItem CurrentScript
		{
			get { return m_CurrentIndex >= 0 ? m_Scripts[m_CurrentIndex] : ScriptListItem.Empty; }
		}

		private ScriptListItem NextScript
		{
			get { return m_CurrentIndex + 1 < m_Scripts.Count ? m_Scripts[m_CurrentIndex + 1] : ScriptListItem.Empty; }
		}

		private ScriptListItem PreviousScript
		{
			get { return m_CurrentIndex - 1 >= 0 ? m_Scripts[m_CurrentIndex - 1] : ScriptListItem.Empty; }
		}

		private void FinishCurrent()
		{
			ScriptListItem current = CurrentScript;
			ScriptListItem next = NextScript;
			ScriptListItem prev = PreviousScript;

			if (current.Script == null)
				throw new Exception("Cannot finish current when it is null");

			current.Script.Ended();
			m_Scripts.RemoveAt(m_CurrentIndex);

			// activate same level script, and lower level ones (these should normally be activated when paused though)
			if (next.Script != null && next.Level >= current.Level)
			{
				next.Script.Started();
			}
			else 
			{
				m_CurrentIndex--;

				if (prev.Script != null)
					prev.Script.Resumed();
			}
		}

		private void PauseCurrent()
		{
			ScriptListItem current = CurrentScript;
			ScriptListItem next = NextScript;

			if (current.Script == null)
				throw new Exception("Cannot pause current when it is null");

			if (next.Script != null && next.Level > current.Level)
			{
				current.Script.Paused();
				m_CurrentIndex++;
				next.Script.Started();
			}
		}

		private List<ScriptListItem> m_Scripts = new List<ScriptListItem>();
		private int m_CurrentIndex = -1;
	}
}
